package com.heima.wemedia.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.common.constants.WemediaConstants;
import com.heima.common.exception.CustomException;
import com.heima.model.common.dtos.*;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.common.pojos.wemedia.WmNews;
import com.heima.model.common.pojos.wemedia.WmNewsMaterial;
import com.heima.utils.thread.WmThreadLocalUtil;
import com.heima.wemedia.mapper.WmMaterialMapper;
import com.heima.wemedia.mapper.WmNewsMapper;
import com.heima.wemedia.mapper.WmNewsMaterialMapper;
import com.heima.wemedia.service.WmAutoScanService;
import com.heima.wemedia.service.WmNewsService;
import com.heima.wemedia.service.WmTaskService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class WmNewsServiceImpl extends ServiceImpl<WmNewsMapper, WmNews> implements WmNewsService {

    @Autowired
    private WmNewsMaterialMapper wmNewsMaterialMapper;

    @Autowired
    private WmMaterialMapper wmMaterialMapper;

    @Autowired
    private WmNewsMapper wmNewsMapper;

    @Autowired
    private WmAutoScanService wmAutoScanService;

    @Autowired
    private WmTaskService wmTaskService;

    @Autowired
    private KafkaTemplate<String,String> kafkaTemplate;


    /**
     * 内容列表
     *
     * @param reqDto
     * @return
     */
    @Override
    public ResponseResult pageQuery(WmNewsPageReqDto reqDto) {
        //1. 参数校验
        if (reqDto == null) {
            throw new CustomException(AppHttpCodeEnum.PARAM_INVALID);
        }

        //分页参数校验
        reqDto.checkParam();

        //2. 组装条件wrapper
        LambdaQueryWrapper<WmNews> wrapper = Wrappers.<WmNews>lambdaQuery();
        //2.1 状态
        if (reqDto.getStatus() != null) {
            wrapper.eq(WmNews::getStatus, reqDto.getStatus());
            //wrapper.eq(reqDto.getStatus() != null,WmNews::getStatus,reqDto.getStatus());
        }
        //2.2 搜索关键字：模糊匹配文章标题
        if (StringUtils.isNotEmpty(reqDto.getKeyword())) {
            wrapper.like(WmNews::getTitle, reqDto.getKeyword());
        }
        //2.3 频道列表
        if (reqDto.getChannelId() != null) {
            wrapper.eq(WmNews::getChannelId, reqDto.getChannelId());
        }
        //2.4 发布日期
        if (reqDto.getBeginPubDate() != null && reqDto.getEndPubDate() != null) {
            wrapper.between(WmNews::getPublishTime, reqDto.getBeginPubDate(), reqDto.getEndPubDate());
        }
        //2.5 按创建时间倒序排列
        wrapper.orderByDesc(WmNews::getCreatedTime);

        //3. 构建分页page
        Page<WmNews> page = new Page<>(reqDto.getPage(), reqDto.getSize());

        //4. 分页条件查询
        page(page, wrapper);

        //5. 封装结果返回
        PageResponseResult pr = new PageResponseResult((int) page.getCurrent(), (int) page.getSize(), (int) page.getTotal());
        pr.setData(page.getRecords());

        return pr;
    }

    /**
     * 发布文章
     *
     * @param wmNewsDto
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseResult submit(WmNewsDto wmNewsDto) {
        //0. 参数校验
        if (wmNewsDto == null || StringUtils.isEmpty(wmNewsDto.getTitle())
                || StringUtils.isEmpty(wmNewsDto.getContent())) {
            throw new CustomException(AppHttpCodeEnum.PARAM_INVALID);
        }

        //1. 新增或修改文章信息： wm_news
        //1.1 创建一个实体对象，wmNews
        WmNews news = new WmNews();
        BeanUtils.copyProperties(wmNewsDto, news);
        //1.2 处理文章布局为自动的情况
        if (wmNewsDto.getType() == -1) {
            //先置空后面处理
            news.setType(null);
        }
        //1.3 处理封面图片
        if (wmNewsDto.getImages() != null && wmNewsDto.getImages().size() > 0) {
            //将List集合转为字符串，用','分割
            String images = StringUtils.join(wmNewsDto.getImages(), ",");
            //设置图片信息
            news.setImages(images);

        }
        //1.4 新增或修改文章信息
        saveOrUpdateNews(news);

        //2. 判断本次操作是否为草稿：草稿则结束流程
        if (wmNewsDto.getStatus() == 0) {
            //结束流程
            return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
        }

        //文章与素材的关联关系表：wm_news_material
        //3. 提取文章内容中的素材图片，并维护内容图片与素材表的关联关系
        //3.1 解析文章内容，提取出内容图片url
        List<String> imgsInContent = getImgInContent(wmNewsDto.getContent());
        //3.2 记录文章与素材的关联关系：素材为内容引用
        saveRelationWithContentImg(imgsInContent, news);

        //4. 维护封面图片与素材表的关联关系
        saveRelationWithCoverImg(imgsInContent, wmNewsDto, news);

        //todo: 开启文章的自动审核流程
        //wmAutoScanService.autoScan(news.getId());

        //异步调用添加任务功能，添加一个审核文章的任务
        wmTaskService.addTask(news);

        //返回信息
        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }

    /**
     * 保存封面图片与文章的关联关系
     * 核心职责：
     * 1. 适配封面图片布局自动的情况：
     * a. 当内容图片数量 >= 1 && < 3，封面布局适配：单图
     * b. 当内容图片数量 >= 3，封面布局适配：三图
     * c. 当内容图片数量 == 0，封面布局适配：无图
     * <p>
     * 2. 保存封面图片与文章的关联关系
     *
     * @param imgsInContent 内容图片
     * @param wmNewsDto     包含了封面图片
     * @param news          文章
     */
    private void saveRelationWithCoverImg(List<String> imgsInContent, WmNewsDto wmNewsDto, WmNews news) {

        //获取前端传来的封面图片
        List<String> images = wmNewsDto.getImages();

        //1. 处理封面为自动的情况
        if (wmNewsDto.getType() == -1) {
            //开启自动适配规则
            if (imgsInContent != null && imgsInContent.size() >= 1 && imgsInContent.size() < 3) {
                //单图：截取内容图片的第一张作为封面图片
                images = imgsInContent.stream().limit(1).collect(Collectors.toList());
                //设置类型
                news.setType((short) 1);
            } else if (imgsInContent != null && imgsInContent.size() >= 3) {
                //多图：截图图片的前三章作为封面
                images = imgsInContent.stream().limit(3).collect(Collectors.toList());
                news.setType((short) 3);
            } else if (imgsInContent == null && images.size() == 0) {
                news.setType((short) 0);
            }

            //更新文章信息
            if (images != null && images.size() > 0) {
                //将集合转为string添加到图片中
                news.setImages(StringUtils.join(images, ","));
            }
            //根据文章更新
            updateById(news);
        }

        //2. 保存封面图片与文章的关联关系
        if (images != null && images.size() > 0) {
            //保存图片与文章的关联关系
            saveRelationWithImg(images, news, 1);
        }
    }

    //保存文章内容图片与文章的关联关系
    private void saveRelationWithContentImg(List<String> imgsInContent, WmNews news) {
        //调用保存图片与文章关系
        saveRelationWithImg(imgsInContent, news, 0);  //0：内容引用
    }

    /**
     * 保存文章与素材图片的关联关系
     *
     * @param imgs
     * @param news
     * @param type 0：内容引用   1：封面引用
     */
    private void saveRelationWithImg(List<String> imgs, WmNews news, int type) {
        if (imgs != null && imgs.size() > 0) {
            //1. 根据素材url查询素材id
            //select * from wm_material where url in(????)
            List<WmMaterial> list = wmMaterialMapper.selectList(Wrappers.<WmMaterial>lambdaQuery().in(WmMaterial::getUrl, imgs));

            //使用distinct去重
            List<Integer> materialIds = list.stream().map(WmMaterial::getId).distinct().collect(Collectors.toList());

            //2. 保存素材与文章的关联关系
            wmNewsMaterialMapper.saveRelations(materialIds, news.getId(), (short) type);
        }
    }

    //提取文章内容中的图片
    private List<String> getImgInContent(String content) {
        //将string转为JSONArray
        JSONArray jsonArray = JSONArray.parseArray(content);
        //创建urls数组
        List<String> urls = new ArrayList<>();

        //遍历JSONArray
        for (Object obj : jsonArray) {
            //转为JSONObject
            JSONObject jsonObject = JSONObject.parseObject(obj.toString());
            //如果content中的type的值为image
            if ("image".equals(jsonObject.getString("type"))) {
                //获取value值，即为图片url
                String imgUrl = jsonObject.getString("value");
                //添加进数组中
                urls.add(imgUrl);
            }
        }
        //返回urls数组
        return urls;
    }

    //新增或修改文章信息
    private void saveOrUpdateNews(WmNews news) {
        if (news.getId() == null) {
            //新增
            //通过ThreadLocal获得当前用户id
            Integer userId = WmThreadLocalUtil.get();
            //如果长时间不操作token过期
            if (userId == null) {
                //抛出异常：需要登录
                throw new CustomException(AppHttpCodeEnum.NEED_LOGIN);
            }
            //设置作者id
            news.setUserId(userId);
            //给定创建时间
            news.setCreatedTime(new Date());
            //默认上架
            news.setEnable((short) 1);
            //保存文章到数据库
            save(news);
        } else {
            //修改：清除文章与原有的素材的关联关系
            //delete from wm_news_material where news_id = ?
            wmNewsMaterialMapper.delete(Wrappers.<WmNewsMaterial>lambdaQuery()
                    .eq(WmNewsMaterial::getNewsId, news.getId()));
            //修改：然后再修改当前文章
            updateById(news);
        }
    }

    /**
     * 删除文章
     *
     * @param id
     * @return
     */
    @Transactional
    @Override
    public ResponseResult delNews(Integer id) {
        if (id == null) {
            throw new CustomException(AppHttpCodeEnum.NEWS_ID_CAN_NOT_NULL);
        }
        WmNews news = wmNewsMapper.selectById(id);
        if (news == null) {
            throw new CustomException(AppHttpCodeEnum.NEWS_NOT_EXIST);
        }
        if (news.getStatus() == WemediaConstants.IS_PUBLISHED) {
            throw new CustomException(AppHttpCodeEnum.NEWS_CAN_NOT_DELETE);
        }
        if (news.getStatus() == WemediaConstants.IS_DRAFT) {
            removeById(id);
        } else {
            wmNewsMaterialMapper.delete(Wrappers.<WmNewsMaterial>lambdaQuery()
                    .eq(WmNewsMaterial::getNewsId, news.getId()));
            removeById(id);
        }
        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }

    /**
     * 文章信息回显
     *
     * @param id
     * @return
     */
    @Override
    public ResponseResult showNews(Integer id) {
        WmNews news = getById(id);
        if (news == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.NEWS_NOT_EXIST);
        }
        return ResponseResult.okResult(news);
    }

    /**
     * 文章上下架
     *
     * @param wmNewsDto
     * @return
     */
    @Override
    public ResponseResult downOrUp(WmNewsDto wmNewsDto) {
        //0. 参数校验
        if (wmNewsDto == null || wmNewsDto.getId() == null
                || wmNewsDto.getEnable() == null) {
            throw new CustomException(AppHttpCodeEnum.PARAM_INVALID);
        }

        //1. 根据id查询文章
        WmNews wmNews = getById(wmNewsDto.getId());

        //2. 校验文章是否存在
        if (wmNews == null){
            throw new CustomException(AppHttpCodeEnum.NEWS_NOT_EXIST);
        }

        //3. 校验文章状态是否为已发布：9
        if (wmNews.getStatus() != WmNews.Status.PUBLISHED.getCode()){
            throw new CustomException(AppHttpCodeEnum.NEWS_CAN_NOT_DOWN_OR_UP);
        }

        //4. 修改文章的enable
        wmNews.setEnable(wmNewsDto.getEnable());
        updateById(wmNews);

        //5. 发送消息到kafka,通知article微服务进行app文章的下架
        Map<String, Object> map = new HashMap<>();
        map.put("article_id",wmNews.getArticleId());
        map.put("enable",wmNews.getEnable());   //0：下架  1：上架
        kafkaTemplate.send("article_down_or_up", JSON.toJSONString(map));
        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }
}
